
/***************************************************************************************************/
#include <NimBLEDevice.h>

NimBLEAdvertisedDevice* advDevice;
bool doConnect = false;
uint32_t scanTime = 0; /** 0 = scan forever */
bool scanning = false, connected = false;
NimBLEAddress Address("04:03:23:68:8b:b0");                      // 蓝牙手柄地址（每个手柄地址都不一样），需自行根据实际地址修改
// NimBLEAddress Address("0B9A8E39-592E-1DAB-643F-BC35C6E22DB7");
NimBLEUUID ServiceUUID("91680001-1111-6666-8888-0123456789AB");  // 蓝牙手柄有数据输出的服务UUID
/***************************************************************************************************/

/***************************************************************************************************/
#include "driver/uart.h"

#define RX1_BUF_SIZE 		(256)
#define TX1_BUF_SIZE 		(256)
#define TXD1_PIN 			(GPIO_NUM_0)
#define RXD1_PIN 			(GPIO_NUM_1)
/***************************************************************************************************/

/***************************************************************************************************/
class ClientCallbacks : public NimBLEClientCallbacks {
  void onConnect(NimBLEClient* pClient) {
    Serial.println("链接成功");
    connected = true;
    digitalWrite(8, 0);
    digitalWrite(9, 1);
  };

  void onDisconnect(NimBLEClient* pClient) {
    Serial.println("链接失败");
    connected = false;
  };
};

/* 定义一个类来处理接收到广告时的回调 */
class AdvertisedDeviceCallbacks : public NimBLEAdvertisedDeviceCallbacks {
  void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
    Serial.print("Advertised Device found: ");
    Serial.println(advertisedDevice->toString().c_str());
    if (advertisedDevice->getAddress() == Address) {
      NimBLEDevice::getScan()->stop();
      advDevice = advertisedDevice;
      doConnect = true;
    }
  };
};

/** Notification / Indication receiving handler callback */
void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) {
  uart_write_bytes(UART_NUM_1, (char*)pData, length);
}

/** 回调处理最后一次扫描的结果或重新启动 */
void scanEndedCB(NimBLEScanResults results) {
  scanning = false;  //扫描状态 = 假
}

void charaSubscribeNotification(NimBLERemoteCharacteristic* pChara)  // 订阅通知函数
{
  if (pChara->canNotify()) {
    Serial.println("canNotify");
    if (pChara->subscribe(true, notifyCB, true))  // 订阅通知
    {
      Serial.println("set NotifyCb");
    }
  }
}

bool afterConnect(NimBLEClient* pClient) {
  Serial.println("FAN0");
  for (auto pService : *pClient->getServices(true)) {
    auto sUuid = pService->getUUID();  // 逐一获取服务中的UUID

    if (!sUuid.equals(ServiceUUID))  // 判断是否有所需的目标UUID
    {
      // Serial.println("f1");
      continue;  // 没有，则直接跳出函数体
    }
    for (auto pChara : *pService->getCharacteristics(true))  // 遍历服务Characteristics
    {
      // Serial.println("f2");
      charaSubscribeNotification(pChara);  // 判读服务特征是否有Notify属性，并订阅
    }
  }
  return true;
}

static ClientCallbacks clientCB;
/** 处理客户端供应和连接/接口与服务器 */
bool connectToServer(NimBLEAdvertisedDevice* advDevice) {
  NimBLEClient* pClient = nullptr;
  /** 创建一个回调类的全局实例供所有客户端使用 */

  /** 检查我们是否有一个客户端，我们应该首先重用 **/
  if (NimBLEDevice::getClientListSize()) {
    pClient = NimBLEDevice::getClientByPeerAddress(advDevice->getAddress());
    if (pClient) {
      pClient->connect();
    }
  }

  if (!pClient) {
    pClient = NimBLEDevice::createClient();
    Serial.println("New client created");
    pClient->setClientCallbacks(&clientCB, false);
    pClient->setConnectTimeout(5);
    pClient->connect(advDevice, false);
  }

  Serial.print("Connected to:");
  Serial.println(pClient->getPeerAddress().toString().c_str());

  bool result = afterConnect(pClient);
  if (!result) {
    return result;
  }
  return true;
}

void startScan() {
  scanning = true;
  auto pScan = NimBLEDevice::getScan();
  pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks());
  pScan->setInterval(45);
  pScan->setWindow(15);
  pScan->start(scanTime, scanEndedCB);
}
/***************************************************************************************************/

/***************************************************************************************************/
void uart1_init(void)
{
	//串口配置结构体
	uart_config_t  uart1_config;
	uart1_config.baud_rate = 115200*8;					    //波特率
	uart1_config.data_bits = UART_DATA_8_BITS;			//数据位
	uart1_config.parity = UART_PARITY_DISABLE;			//校验位
	uart1_config.stop_bits = UART_STOP_BITS_1;			//停止位
	uart1_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;	//硬件流控
	uart_param_config(UART_NUM_1, &uart1_config);		//设置串口
	//IO映射-> T:IO12  R:IO13
	uart_set_pin(UART_NUM_1, TXD1_PIN, RXD1_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
	//注册串口服务即使能+设置缓存区大小
	uart_driver_install(UART_NUM_1, RX1_BUF_SIZE * 2, TX1_BUF_SIZE * 2, 0, NULL, 0);
}
/***************************************************************************************************/
void setup() {
  Serial.begin(115200);
  Serial.println("Starting NimBLE Client");
  /** Initialize NimBLE, no device name spcified as we are not advertising */
  NimBLEDevice::init("SGF");
  NimBLEDevice::setPower(ESP_PWR_LVL_N0); /* 板载陶瓷天线仅支持 ESP_PWR_LVL_N0 功率 */
  startScan();

  uart1_init();
  
  pinMode(8, OUTPUT);  //指示灯1
  pinMode(9, OUTPUT);  //指示灯2
  digitalWrite(8, 1);
  digitalWrite(9, 1);
}

uint16_t led_cnt = 0;
uint8_t led_flag = 0;

void loop() 
{
  while (!doConnect) 
  {
    delay(1);
    if (++led_cnt >= 200) 
    {
      led_cnt = 0;
      if (led_flag == 0) 
      {
        led_flag = 1;
        digitalWrite(9, 1);
      }
      else if (led_flag == 1) 
      {
        led_flag = 0;
        digitalWrite(9, 0);
      }
    }
  }
  if (!connected) 
  {
    delay(1);
    digitalWrite(8, 1);
    if (++led_cnt >= 200) 
    {
      led_cnt = 0;
      if (led_flag == 0) 
      {
        led_flag = 1;
        digitalWrite(9, 1);
      } 
      else if (led_flag == 1) 
      {
        led_flag = 0;
        digitalWrite(9, 0);
      }
    }
    if (advDevice != nullptr) 
    {
      if (connectToServer(advDevice)) 
      {
        Serial.println("ShouBing OK");
      }
      advDevice = nullptr;
    } 
    else if (!scanning) 
    {
      startScan();
    }
  }
}
